﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Upreader.Common
{
    /// <summary>
    /// Aggregates a sequence of streams into 1 single stream
    /// </summary>
    public class AggregateStream : Stream
    {
        #region Fields

        private readonly IEnumerable<Stream> _innerStreams;

        private IEnumerator<Stream> _innerStreamsEnumerator;
        private bool _isCurrentAvailable;

        #endregion // Fields

        #region Constructor

        public AggregateStream(IEnumerable<Stream> innerStreams)
        {
            if (innerStreams == null)
                throw new ArgumentNullException("innerStreams");

            _innerStreams = innerStreams;
        }

        #endregion // Constructor

        #region Properties

        public IEnumerable<Stream> InnerStreams
        {
            get { return _innerStreams; }
        }       

        /// <summary>
        /// Always true
        /// </summary>
        public override bool CanRead
        {
            get { return true; }
        }

        /// <summary>
        /// Always false
        /// </summary>
        public override bool CanSeek
        {
            get { return false; }
        }

        /// <summary>
        /// Always false
        /// </summary>
        public override bool CanWrite
        {
            get { return false; }
        }

        public override void Flush()
        {
            // no implementation
        }

        /// <summary>
        /// Not supported
        /// </summary>
        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        /// <summary>
        /// Not supported
        /// </summary>
        public override long Position
        {
            get
            {
                throw new NotSupportedException();
            }
            set
            {
                throw new NotSupportedException();
            }
        }

        #endregion // Properties

        #region Methods

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer");
            if (offset < 0)
                throw new ArgumentOutOfRangeException("offset");
            if (count < 0)
                throw new ArgumentOutOfRangeException("count");
            if (count > buffer.Length - offset)
                throw new ArgumentOutOfRangeException("count", "to many bytes were requested for the current buffer when starting from offset");

            // if innerStreamsEnumerator is empty, this is a initial case
            if (_innerStreamsEnumerator == null)
            {
                _innerStreamsEnumerator = _innerStreams.GetEnumerator();
                _isCurrentAvailable = _innerStreamsEnumerator.MoveNext();
            }

            int total = 0;

            while (_isCurrentAvailable)
            {
                Stream currentStream = _innerStreamsEnumerator.Current;
                total += currentStream.Read(buffer, offset + total, count - total);

                if (total != count)
                {
                    _isCurrentAvailable = _innerStreamsEnumerator.MoveNext();
                }
            }

            return total;
        }

        /// <summary>
        /// Not supported
        /// </summary>
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Not supported
        /// </summary>
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Not supported
        /// </summary>
        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        #endregion // Methods
    }
}
